/*
 * ==========================================================================================================
 * 
 *                                              Multi render demo
 *                                              
 * ==========================================================================================================
 * 
 * This demo shaw the multi render capability of DARE.
 * The first render contains a lot of physics objects, the second only a heightmap and the third two 
 * physics objects.
 * Switching render is instant since they are already loaded in memory.
 * There is no fps drop when creating multiple renders, only the visible one is uptated and drawn on screen.
 * 
 * 
 * 
 * - Engine control
 * Espace - quit
 * F1 - view console
 * F2 - toggle fullscreen mode
 * F3 - antialiasing
 * F4 - view node model
 * F5 - view collisions
 * F6 - collision fillmode
 * F7 - view partitioner
 * 
 * - Demo control
 * 1 - activate render 0
 * 2 - activate render 1
 * 3 - activate render 2
 * W - move forward
 * S - move backward
 * A - strafe left
 * D - strafe right
 * Left shift - move down
 * Space - move up
 * Mouse move - look
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace MultiRender
{

    public class Game1 : Microsoft.Xna.Framework.Game
    {

    #region fields

        private DARE.CDare m_Dare = null;
        private DARE.CScene m_Scene = null;

        private int m_indexRender = 0;

        private Vector3 m_moveCam = Vector3.Zero;
        private DARE.CFirstPersonCamera m_firstCam;

        private DARE.CNode m_pointer = null;

        private bool m_rightButtonPressed = false;
        private bool m_leftButtonPressed = false;

        private List<DARE.CNode> m_dynamicObjects = new List<DARE.CNode>();

    #endregion

    #region ctor

        public Game1()
        {
            m_Dare = new DARE.CDare(this);
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

    #endregion

    #region load

        protected override void LoadContent()
        {
            m_Dare.Config.General.WindowTitle = "Multi render";
            m_Dare.Config.General.ShowMouseCursor = true;
            m_Dare.Config.General.ShowDebugConsole = true;

            m_Dare.Config.Video.FullScreen = false;
            m_Dare.Config.Video.SetResolution(1024, 768);
            m_Dare.Config.Video.MultiSampling = true;
            m_Dare.Config.Video.UnlockFps = true;

            m_Dare.RockNRoll();

            CreateRender();
            CreateCamera();
            CreateScene();
    
            ActivateRender(0);
        }

    #endregion

    #region creation

        private void CreateRender()
        {
            int renderNumber = 3;

            // create our scene
            m_Scene = new DARE.CScene("scene");

            // create our renders
            for (int i = 0; i < renderNumber; ++i)
                m_Scene.RegisterRenderer(new DARE.CRender3D("render" + i.ToString(), new DARE.COctree(500)));

            // select our current render
            m_Scene.ActiveRender3D = m_Scene.Render3D.ElementAt(m_indexRender).Value;
#if !WINDOWS_PHONE
            m_Dare.DebugMgr.DebugConsole.WriteLine("Active render: " + m_indexRender);
#endif
        }

        private void CreateCamera()
        {
            // create a first person camera for each of our render, 
            // we could also use the same camera for all, but it will move even on 
            // inactive renders.
            foreach (DARE.CRender3D render in m_Scene.Render3D.Values)
            {
                DARE.CFirstPersonCamera camera = new DARE.CFirstPersonCamera("firstCam");
                camera.Translate(new Vector3(10, 63, 34));
                camera.LookAt(new Vector3(20, 5, -25));
                camera.FarPlane = 250.0f;

                render.CameraMgr.RegisterCamera(camera);
            }

            // select our current camera
            m_firstCam = m_Scene.ActiveRender3D.CameraMgr.ActiveCamera as DARE.CFirstPersonCamera;
        }

        private void CreateScene()
        {
            int renderNumber = 0;

            foreach (DARE.CRender3D render in m_Scene.Render3D.Values)
            {
                // ========== same base for each render ==========

                // show option
                render.ShowModelNode = false;
                render.ShowCollision = true;
                render.Partitioner.Show = false;
                render.CollisionFillMode = FillMode.Solid;

                // light
                render.EffectMgr.LighteningEnabled = true;
                render.EffectMgr.AmbientLightColor = Color.White;
                render.EffectMgr.AmbientLightIntensity = 0.1f;

                render.EffectMgr.DirectionalLight0Direction = new Vector3(0.6f, -1, -0.8f);
                render.EffectMgr.DirectionalLight0DiffuseColor = Color.White;
                render.EffectMgr.DirectionalLight0DiffuseIntensity = 1.5f;

                // pointer
                m_pointer = render.CreateNode("nPointer", DARE.EType.DYNAMIC, true);
                m_pointer.Entity.CreateBasicSphere(0.3f);
                ((DARE.CBasicModel)m_pointer.Entity.Model).Color = Color.Red;

                // floor
                DARE.CNode floor = render.CreateNode("nFloor", DARE.EType.STATIC, true);
                floor.Entity.AttachModel(new DARE.CFloorModel());
                // switching those two lines ignore the wireframe o.O
                floor.AABB = new BoundingBox(new Vector3(0, -0.1f, -50), new Vector3(50, 0.1f, 0));
                floor.Entity.Model.FillMode = FillMode.WireFrame;

                // physics floor
                //DARE.CEntity en = render.CreateNode().Entity.CreatePhysicBox(40, 1, 40);
                //en.Physic.Body.Immovable = true;

                // sky
                render.SkyMgr.CreateSkybox("sky", "sky/ct");

                // ========== create some differencies ========

                for (int i = 0; i <= renderNumber; ++i)
                    render.CreateNode(DARE.EType.STATIC, true).SetPosition(1 + i * 2, 0, -1, DARE.CNode.ESpace.WORLD).Entity.CreateBasicBox();

                renderNumber += 1;
            }

            {
                DARE.CRender3D render = null;
                int i = -1;

                if (++i < renderNumber)
                    render = m_Scene.Render3D.ElementAt(i).Value;
                else return;

                // ========================== render 0 ==========================
                {
                    m_Dare.DebugMgr.DebugConsole.WriteLine("Creating content for render " + i.ToString() + ".");

                    DARE.CNode node = null;

                    node = render.CreateNode("n0", DARE.EType.STATIC, true);
                    node.Position = new Vector3(25, 25, -25);
                    node.Orientation = Quaternion.CreateFromYawPitchRoll(0, 0, MathHelper.PiOver4);
                    node.Entity.CreatePhysicBox(10, 1, 10);
                    node.Entity.Physic.Body.Immovable = true;

                    node = render.CreateNode("n1", DARE.EType.STATIC, true);
                    node.Position = new Vector3(10, 10, -25);
                    node.Entity.CreatePhysicSphere(15);
                    node.Entity.Physic.Body.Immovable = true;

                    node = render.CreateNode("n2", DARE.EType.STATIC, true);
                    node.Position = new Vector3(30, 0, -25);
                    node.Yaw(MathHelper.PiOver4);
                    node.Entity.CreatePhysicBox(10, 10, 10);
                    node.Entity.Physic.Body.Immovable = true;

                    for (int j = 0; j < 200; ++j)
                    {
                        node = render.CreateNode(DARE.EType.DYNAMIC, true);
                        node.Position = RandomPosition();

                        switch (DARE.CDare.Instance.Random.Next(0, 4))
                        {
                            case 0: node.Entity.CreatePhysicBox(); break;
                            case 1: node.Entity.CreatePhysicSphere(); break;
                            case 2: node.Entity.CreatePhysicCylinder(); break;
                            case 3: node.Entity.CreatePhysicCapsule(); break;
                            default: break;
                        }

                        m_dynamicObjects.Add(node);
                    }

                    int nbBodies = render.PhysicsSystem.Bodies.Count;

                    m_Dare.DebugMgr.DebugConsole.WriteLine("Number of bodies: " + nbBodies.ToString());
                }
                // ==============================================================

                if (++i < renderNumber)
                    render = m_Scene.Render3D.ElementAt(i).Value;
                else return;

                // ========================== render 1 ==========================
                {
                    m_Dare.DebugMgr.DebugConsole.WriteLine("Creating content for render " + i.ToString() + ".");

                    DARE.CNode node = null;

                    // hu
                    node = render.CreateNode("n1", DARE.EType.STATIC, true);
                    node.Position = new Vector3(25, 25, -25);
                    node.Orientation = Quaternion.CreateFromYawPitchRoll(0, MathHelper.PiOver4, 0);
                    node.Entity.CreatePhysicBox(10, 1, 10);
                    node.Entity.Physic.Body.Immovable = true;

                    // terrain
                   // DARE.CTerrainInfo terrainInfo = new DARE.CTerrainInfo(
                        //DARE.CResourceMgr.Load<Texture2D>("Heightmap/land_0"), 30, 1);
                    //DARE.CTerrainModel terrainModel = new DARE.CTerrainModel(terrainInfo, 16, 16);
                    //node = render.AddNode(terrainModel.RootNode).SetName("nTerrain1");
                    //node.Position = new Vector3(0, -28, 0);
                    //node.Entity.CreatePhysicTerrain(terrainInfo);

                    int nbBodies = render.PhysicsSystem.Bodies.Count;

                    m_Dare.DebugMgr.DebugConsole.WriteLine("Number of bodies: " + nbBodies.ToString());
                }
                // ==============================================================

                if (++i < renderNumber)
                    render = m_Scene.Render3D.ElementAt(i).Value;
                else return;

                // ========================== render 2 ==========================
                {
                    m_Dare.DebugMgr.DebugConsole.WriteLine("Creating content for render " + i.ToString() + ".");

                    DARE.CNode node = null;

                    node = render.CreateNode("n1", DARE.EType.STATIC, true);
                    node.Position = new Vector3(25, 15, -25);
                    node.Orientation = Quaternion.CreateFromYawPitchRoll(0, 0, -MathHelper.PiOver4);
                    node.Entity.CreatePhysicSphere(10);
                    node.Entity.Physic.Body.Immovable = true;

                    node = render.CreateNode(DARE.EType.DYNAMIC, true);
                    node.Position = RandomPosition();
                    node.Entity.CreatePhysicBox(10, 10, 10);
                    m_dynamicObjects.Add(node);

                    int nbBodies = render.PhysicsSystem.Bodies.Count;

                    m_Dare.DebugMgr.DebugConsole.WriteLine("Number of bodies: " + nbBodies.ToString());
                }
                // ==============================================================
            }
        }

    #endregion

    #region run

        private Vector3 RandomPosition()
        {
            return new Vector3(
                        DARE.CDare.Instance.Random.Next(22, 28),
                        DARE.CDare.Instance.Random.Next(50, 71),
                        DARE.CDare.Instance.Random.Next(-27, -23));
        }

        protected override void Update(GameTime gameTime)
        {
            ProcessKeyPressed();
            ProcessKeyReleased();
            ProcessMousePressed();
            ProcessMouseReleased();
            ProcessMouseMoved();
            ProcessMouseWheeled();
            ProcessRayCastCameraToMouse();
            ApplyMovement();

            //m_Dare.DebugMgr.DebugConsole.WriteLine("tslf = " + m_Dare.Config.Time.FixedTimeSinceLastFrame.ToString());
            //m_Dare.DebugMgr.DebugConsole.WriteLine("fps  = " + m_Dare.Config.Time.FPS.ToString());

            foreach (DARE.CNode node in m_dynamicObjects)
            {
                if (node.Position.Y < -20)
                {
                    //node.IgnorePhysicsTransform = true;
                    node.Entity.Physic.Body.MoveTo(RandomPosition(), Matrix.Identity);
                    //node.Position = new Vector3(25, 80, -25);
                    //node.IgnorePhysicsTransform = false;
                }
            }

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);
        }

        private void ApplyMovement()
        {
            float fixedTime = m_Dare.Config.Time.FixedTimeSinceLastFrame;
            float fixedMoveSpeed = fixedTime / 100.0f;

            if (m_rightButtonPressed && m_firstCam.IsActive)
                m_firstCam.Translate(m_moveCam * fixedMoveSpeed);
        }

        private void ProcessRayCastCameraToMouse()
        {
            Vector3 firstPos = Vector3.Zero;

            foreach (DARE.CRayCast.CIntersectInfo iInfo in m_Scene.ActiveRender3D.RayCast.IntersectAllFromCenterToMouse(100, 5))
            {
                if (firstPos == Vector3.Zero)
                    firstPos = iInfo.pos;

                switch (iInfo.node.Entity.Physic.CollisionType)
                {
                    case DARE.APhysicObject.ECollisionType.FADE:
                        if (iInfo.node.Entity.Model != null) iInfo.node.Entity.Model.Fade(); break;

                    case DARE.APhysicObject.ECollisionType.GROUND:
                        break;

                    default: break;
                }
            }

            if (m_pointer != null)
                m_pointer.Position = firstPos;
        }

    #endregion

    #region input

        private void ProcessKeyPressed()
        {
            foreach (Keys key in m_Dare.InputMgr.Keyboard.KeyPressedEvent)
                switch (key)
                {
                    case Keys.Escape: this.Exit(); break;
                    case Keys.F1: m_Dare.DebugMgr.DebugConsoleEnabled = !m_Dare.DebugMgr.DebugConsoleEnabled; break;
                    case Keys.F2: m_Dare.Config.Video.FullScreen = !m_Dare.Config.Video.FullScreen; break;
                    case Keys.F3: m_Dare.Config.Video.MultiSampling = !m_Dare.Config.Video.MultiSampling; break;
                    case Keys.F4: m_Scene.ActiveRender3D.ShowModelNode = !m_Scene.ActiveRender3D.ShowModelNode; break;
                    case Keys.F5: m_Scene.ActiveRender3D.ShowCollision = !m_Scene.ActiveRender3D.ShowCollision; break;
                    case Keys.F6: m_Scene.ActiveRender3D.CollisionFillMode = m_Scene.ActiveRender3D.CollisionFillMode == FillMode.WireFrame ? FillMode.Solid : FillMode.WireFrame; break;
                    case Keys.F7: m_Scene.ActiveRender3D.Partitioner.Show = !m_Scene.ActiveRender3D.Partitioner.Show; break;

                    case Keys.D1: ActivateRender(0); break;
                    case Keys.D2: ActivateRender(1); break;
                    case Keys.D3: ActivateRender(2); break;

                    case Keys.W: m_moveCam.Z -= 1; break;
                    case Keys.S: m_moveCam.Z += 1; break;
                    case Keys.A: m_moveCam.X -= 1; break;
                    case Keys.D: m_moveCam.X += 1; break;
                    case Keys.LeftShift: m_moveCam.Y -= 1; break;
                    case Keys.Space: m_moveCam.Y += 1; break;

                    default: break;
                }
        }

        private void ActivateRender(int index)
        {
            m_Dare.DebugMgr.DebugConsole.WriteLine("\nActivating render " + index.ToString() + "...");

            DARE.CRender3D render = m_Scene.Render3D.ElementAt(index).Value;

            m_Scene.ActiveRender3D = render;

            m_firstCam = render.CameraMgr.ActiveCamera as DARE.CFirstPersonCamera;
            m_pointer = render.GetNode("nPointer");

            int nbNodes = render.NodeNumber;
            int nbBodies = render.PhysicsSystem.Bodies.Count;

            m_Dare.DebugMgr.DebugConsole.WriteLine("Number of nodes: " + nbNodes.ToString());
            m_Dare.DebugMgr.DebugConsole.WriteLine("Number of physical bodies: " + nbBodies.ToString());
        }

        private void ProcessKeyReleased()
        {
            foreach (Keys key in m_Dare.InputMgr.Keyboard.KeyReleasedEvent)
                switch (key)
                {
                    case Keys.W: m_moveCam.Z += 1; break;
                    case Keys.S: m_moveCam.Z -= 1; break;
                    case Keys.A: m_moveCam.X += 1; break;
                    case Keys.D: m_moveCam.X -= 1; break;
                    case Keys.LeftShift: m_moveCam.Y += 1; break;
                    case Keys.Space: m_moveCam.Y -= 1; break;

                    default: break;
                }
        }

        private void ProcessMousePressed()
        {
            foreach (DARE.CMouse.EButton button in m_Dare.InputMgr.Mouse.ButtonPressedEvent)
            {
                switch (button)
                {
                    case DARE.CMouse.EButton.RIGHT: m_rightButtonPressed = true; break;
                    case DARE.CMouse.EButton.LEFT: m_leftButtonPressed = true; break;

                    default: break;
                }
                m_Dare.InputMgr.Mouse.IsCentered = m_rightButtonPressed;
                m_Dare.Config.General.ShowMouseCursor = !m_rightButtonPressed;
            }
        }

        private void ProcessMouseReleased()
        {
            foreach (DARE.CMouse.EButton button in m_Dare.InputMgr.Mouse.ButtonReleasedEvent)
            {
                switch (button)
                {
                    case DARE.CMouse.EButton.RIGHT: m_rightButtonPressed = false; break;
                    case DARE.CMouse.EButton.LEFT: m_leftButtonPressed = false; break;

                    default: break;
                }
                m_Dare.InputMgr.Mouse.IsCentered = m_rightButtonPressed;
                m_Dare.Config.General.ShowMouseCursor = !m_rightButtonPressed;
            }
        }

        private void ProcessMouseMoved()
        {
            Vector2 mouseMoved = m_Dare.InputMgr.Mouse.MoveEvent.Movement;

            if (m_Dare.InputMgr.Mouse.HasMoved && m_rightButtonPressed && m_firstCam.IsActive)
                m_firstCam.Rotate(new Vector3(-mouseMoved.Y / 400.0f, -mouseMoved.X / 400.0f, 0.0f));
        }

        private void ProcessMouseWheeled()
        {
            List<DARE.CMouseWheelEvent> mev = m_Dare.InputMgr.Mouse.WheelEvent;

            foreach (DARE.CMouseWheelEvent e in mev)
            {
                if (e.WheelValue > 0)
                {
                    m_indexRender += 1;
                    if (m_indexRender >= m_Scene.Render3D.Count)
                        m_indexRender = 0;
                }
                else if (e.WheelValue < 0)
                {
                    m_indexRender -= 1;
                    if (m_indexRender < 0)
                        m_indexRender = m_Scene.Render3D.Count - 1;
                }
                ActivateRender(m_indexRender);
            }
        }

    #endregion

    }
}
